package com.jcfk.system.service;

import cn.hutool.core.date.DateUtil;
import com.jcfk.common.business.enums.DeleteFlag;
import com.jcfk.common.cache.redis.service.RedisHashService;
import com.jcfk.common.cache.redis.service.RedisLock;
import com.jcfk.common.cache.redis.service.RedisStringService;
import com.jcfk.system.domain.dto.CodeBuildRequest;
import com.jcfk.system.domain.po.SysCodeRule;
import com.jcfk.system.domain.query.SysCodeRoleItemQuery;
import com.jcfk.system.domain.query.SysCodeRuleQuery;
import com.jcfk.system.domain.dto.SysCodeRuleDTO;
import com.jcfk.system.domain.dto.SysCodeRoleItemDTO;
import com.jcfk.system.domain.po.SysCodeRoleItem;
import com.jcfk.system.dao.SysCodeRuleDao;
import com.jcfk.common.business.service.BaseService;
import com.jcfk.common.web.domain.DataPage;
import com.jcfk.common.web.domain.RetResult;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import com.jcfk.common.core.utils.BeanUtils;
import cn.hutool.core.util.StrUtil;
import com.jcfk.common.security.utils.SecurityUtils;
import com.alibaba.druid.util.StringUtils;

import javax.annotation.PostConstruct;

import static com.jcfk.common.core.utils.BeanUtils.convertList;

import java.util.*;

import java.util.stream.Collectors;

/**
 * 编码生成规则
 *
 * @author libinghai
 * @email 35738025@gqq.com
 * @date 2024-04-11
 */
@Slf4j
@Service
public class SysCodeRuleService extends BaseService<SysCodeRuleDao,SysCodeRule> {

    /**
     * Redis 缓存键名
     */
    private static final String redisKeyName = "SysCodeRule";

    @Autowired
    private RedisHashService redisHashService;

    @Autowired
    private RedisStringService redisStringService;

    @Autowired
    private SysCodeRoleItemService sysCodeRoleItemService;

    /**
     * SysCodeRule分页查询
     *
     * @param page  分页设置
     * @param query 查询条件
     * @return 分页查询结果
     */
    public DataPage<SysCodeRuleDTO> queryByPage(DataPage<SysCodeRuleDTO> page, SysCodeRuleQuery query) {
        // 分页查询
    query.setRuleName(StringUtils.isEmpty(query.getRuleName()) ? query.getRuleName() :"%" + query.getRuleName() + "%");
        page = this.baseMapper.queryByPage(page, query);
        return page;
    }

    /**
     * 根据条件获取单表信息列表
     *
     * @param query 查询条件
     * @return List<SysCodeRuleDTO>
     */
    public List<SysCodeRuleDTO> getList(SysCodeRuleQuery query) {
        // 查询条件
        QueryWrapper<SysCodeRule> qw = new QueryWrapper<>();
        qw.lambda().eq(!StrUtil.isEmpty(query.getRuleCode()), SysCodeRule::getRuleCode, query.getRuleCode());
        qw.lambda().like(!StrUtil.isEmpty(query.getRuleName()), SysCodeRule::getRuleName, query.getRuleName());
        // 查询数据
        List<SysCodeRule> list = super.list(qw);

        return convertList(list, SysCodeRuleDTO.class);
    }

    /**
     * 获取根据关联id详情表列表
     * @param query 查询条件
     * @return List<SysCodeRuleDTO>
     */
    public List<SysCodeRoleItemDTO> getDetails(SysCodeRuleQuery query) {
        // 查询条件
        QueryWrapper<SysCodeRoleItem> qw = new QueryWrapper<>();
        qw.lambda().eq(!StrUtil.isEmpty(query.getId()), SysCodeRoleItem::getRuleId, query.getId());
        qw.lambda().orderByAsc(SysCodeRoleItem::getSeq);

        // 查询数据
        List<SysCodeRoleItem> list = sysCodeRoleItemService.list(qw);

        return convertList(list, SysCodeRoleItemDTO.class);
    }



    /**
     * 判断SysCodeRule是已经存在
     *
     * @param dto
     * @return
     */
    public RetResult<String> isValid(SysCodeRuleDTO dto) {
        QueryWrapper<SysCodeRule> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("deleted", 0);
        return RetResult.ok();
    }

    /**
     * 插入编码生成规则
     *
     * @param dto 插入对象
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public void insert(SysCodeRuleDTO dto) {

        // 保存
        SysCodeRule item = new SysCodeRule();
        BeanUtils.copyProperties(dto, item);
        this.setDefaultValue(item);

        super.save(item);

        // 设置关联主键
        for (SysCodeRoleItemDTO detail : dto.getDetails()) {
            detail.setRuleId(item.getId());
        }

        // 保存明细表
        sysCodeRoleItemService.saveBatch(dto.getDetails(),dto.getId());

        //添加缓存
        redisHashService.setMapValue(redisKeyName,dto.getRuleCode(),dto);
    }


    /**
     * 更新编码生成规则
     *
     * @param dto 更新对象
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public void update(SysCodeRuleDTO dto) {

        // 保存
        SysCodeRule item = new SysCodeRule();
        BeanUtils.copyProperties(dto, item);

        // 默认数据
        this.setDefaultValue(item);

        super.updateById(item);

        // 设置关联主键
        for (SysCodeRoleItemDTO detail : dto.getDetails()) {
            detail.setRuleId(dto.getId());
        }
        // 保存明细表
        sysCodeRoleItemService.saveBatch(dto.getDetails(),dto.getId());

        //更新缓存
        redisHashService.setMapValue(redisKeyName,dto.getRuleCode(),dto);
    }

    /**
     * 根据批量主键删除编码生成规则(逻辑删除)
     *
     * @param dto
     * @return bool
     */
    @Transactional(rollbackFor = Exception.class)
    public Boolean logicDeleteList(SysCodeRuleDTO dto) {

        SysCodeRule item = new SysCodeRule();

        this.setDefaultValue(item);
        item.setVersion(dto.getVersion());
        item.setDeleted(1);

        QueryWrapper<SysCodeRule> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("id", dto.getIds());

        Boolean result=this.update(item, queryWrapper);

        for(String id : dto.getIds()){
            SysCodeRule ruleDTO=this.baseMapper.selectById(id);
            //删除缓存
            redisHashService.removeMapValue(redisKeyName,ruleDTO.getRuleCode());
        }

        return result;
    }

    /**
     * 设置添加默认值
     *
     * @param item 添加对象
     */
    public void setDefaultValue(SysCodeRule item) {
        if (StrUtil.isEmpty(item.getCreatedUserId())) {
            item.setCreatedUserId(SecurityUtils.getUserId());
            item.setCreateTime(new Date());
        }
        item.setLastUpdatedUserId(SecurityUtils.getUserId());
        item.setLastUpdateTime(new Date());
        item.setDeleted(0);
    }


    /**
     * 根据主键获取编码生成规则信息
     *
     * @param id 主键
     * @return StandardCodeDTO
     */
    public SysCodeRuleDTO get(String id) {
        // 查询数据
        SysCodeRule item = this.baseMapper.selectById(id);
            SysCodeRuleDTO dto = new SysCodeRuleDTO();
        try {
            BeanUtils.copyProperties(item, dto);
        } catch (RuntimeException e) {
            throw e;
        }

        return dto;
    }

    /**
     * 通过登录名获取用户信息
     *
     * @param ruleCode
     * @return
     */
    public SysCodeRuleDTO getByCode(String ruleCode) {
        SysCodeRuleDTO dto=new SysCodeRuleDTO();

        // 查询条件
        QueryWrapper<SysCodeRule> qw = new QueryWrapper<>();
        qw.lambda().eq(SysCodeRule::getDeleted, Integer.parseInt(DeleteFlag.NOTDELETED.getCode()));
        qw.lambda().eq(SysCodeRule::getRuleCode, ruleCode);
        SysCodeRule codeRule = super.getOne(qw);

        SysCodeRoleItemQuery query=new SysCodeRoleItemQuery();
        query.setRuleId(codeRule.getId());
        List<SysCodeRoleItemDTO> details=sysCodeRoleItemService.getList(query);

        BeanUtils.copyProperties(codeRule, dto);
        dto.setDetails(details);
        // 查询数据
        return dto;
    }

    /**
     * 根据接口编号从缓存中获取信息
     * @param ruleCode
     * @return
     */
    public SysCodeRuleDTO getByCache(String ruleCode){
        SysCodeRuleDTO codeRule=redisHashService.getMapValue(redisKeyName,ruleCode);
        if(codeRule==null){
            codeRule=this.getByCode(ruleCode);
            //更新缓存
            redisHashService.setMapValue(redisKeyName,ruleCode,codeRule);
        }
        return codeRule;
    }

    /**
     * 批量生成新编码
     * @param buildRequest
     * @return
     */
    public List<String> batchBuildNewCode(CodeBuildRequest buildRequest){

        List<String> newCodeList=new ArrayList<>();
        String lock=buildRequest.getRuleCode();
        //RedisLock redisLock=new RedisLock(buildRequest.getRuleCode(),3,3000);
        synchronized (lock) {
            try {
                //Boolean lockResult=redisLock.lock();

                //if(lockResult==false) throw new RuntimeException("生成单号出现异常-未锁定");

                for (int i = 0; i < buildRequest.getBuildCount(); i++) {
                    String newCode = this.createNewCode(buildRequest);
                    newCodeList.add(newCode);
                }
                //redisLock.unlock();
            } catch (Exception e) {
                log.error("生成单号出现异常-" + e.getMessage(), e);
            } finally {
//            if(redisLock!=null&&redisLock.isLock()){
//                redisLock.unlock();
//            }
            }
        }

        return newCodeList;
    }

    /**
     * 生成新编码
     * @param buildRequest
     * @return
     */
    public String buildNewCode(CodeBuildRequest buildRequest){

        String newCode="";
        String lock=buildRequest.getRuleCode();
        RedisLock redisLock=new RedisLock(buildRequest.getRuleCode(),3,3000);
        synchronized (lock) {
            try {
                Boolean lockResult=redisLock.lock();

                if(lockResult==false) throw new RuntimeException("生成单号出现异常-未锁定");

                newCode = this.createNewCode(buildRequest);
                redisLock.unlock();
            } catch (Exception e) {
                log.error("生成单号出现异常-" + e.getMessage(), e);
            } finally {
                if(redisLock!=null){
                    redisLock.unlock();
                }
            }
        }

        return newCode;
    }

    /**
     * 生成新编码
     * @param buildRequest
     * @return
     */
    public String createNewCode(CodeBuildRequest buildRequest){

        String newCode="";

        SysCodeRuleDTO codeRule=this.getByCache(buildRequest.getRuleCode());

        //查找自增序号代码段
        int autoNumberIndex=-1;
        Optional<SysCodeRoleItemDTO> autoNumberSection=codeRule.getDetails().stream().filter(p->p.getCodeSectionType().equals("AutoNumber")).findAny();

        if(autoNumberSection.isPresent()==true){
            autoNumberIndex=codeRule.getDetails().indexOf(autoNumberSection.get());
        }

        if(autoNumberIndex==-1){
            //无自增序号段
            newCode=getCodes(codeRule.getDetails(),buildRequest.getDynamicValues());

            setNewCode(codeRule,"",newCode);
        }
        else
        {
            //有自增序号段
            String frontSectionCode=getCodes(codeRule.getDetails().stream().limit(autoNumberIndex).collect(Collectors.toList()), buildRequest.getDynamicValues());

            String lastCode=this.getLastCode(buildRequest.getRuleCode(),frontSectionCode);

            if(StrUtil.isNotEmpty(lastCode)){
                String lastSeqCode=lastCode.substring(frontSectionCode.length());

                int lastSeq=Integer.parseInt(removeLeadingZeros(lastSeqCode))+1;
                String seqCode=String.format("%"+autoNumberSection.get().getCodeFormat()+autoNumberSection.get().getCodeLength()+"d", lastSeq);

                newCode=frontSectionCode+seqCode;
            }
            else
            {
                String seqCode=String.format("%"+autoNumberSection.get().getCodeFormat()+autoNumberSection.get().getCodeLength()+"d", 1);

                newCode=frontSectionCode+seqCode;
            }
            setNewCode(codeRule,frontSectionCode,newCode);
        }

        return newCode;
    }

    private void setNewCode(SysCodeRuleDTO codeRule,String frontSectionCode,String newCode){

        if(StrUtil.isEmpty(frontSectionCode)){
            redisStringService.setValue(redisKeyName+":"+codeRule.getRuleCode()+":newNode",newCode);
        }
        else
        {
            redisStringService.setValue(redisKeyName+":"+codeRule.getRuleCode()+":newNode:"+frontSectionCode,newCode);
        }
    }

    private String getLastCode(String ruleCode,String frontSectionCode){
        String newCode="";
        if(StrUtil.isEmpty(frontSectionCode)){
            newCode=redisStringService.getValue(redisKeyName+":"+ruleCode+":newNode");
        }
        else
        {
            newCode=redisStringService.getValue(redisKeyName+":"+ruleCode+":newNode:"+frontSectionCode);
        }
        return newCode;
    }

    private String getCodes(List<SysCodeRoleItemDTO> details, Map<Integer,String> dynamicValues){
        String code="";
        int dynamicIndex=1;
        for (SysCodeRoleItemDTO item : details){
            if(item.getCodeSectionType().equals("FixedString")){
                code+=item.getCodeFormat();
            }
            if(item.getCodeSectionType().equals("DateTime")){
                code+=DateUtil.format(new Date(),item.getCodeFormat());
            }
            if(item.getCodeSectionType().equals("DynamicString")){
                code+=dynamicValues.get(dynamicIndex);
                dynamicIndex++;
            }
        }
        return code;
    }

    public static String removeLeadingZeros(String inputString) {
        // Return the input string as-is if it's null or empty
        if (inputString == null || inputString.isEmpty()) {
            return inputString;
        }

        // Iterate through the characters in the string until a non-zero character is found
        int firstNonZeroIndex = 0;
        while (firstNonZeroIndex < inputString.length() && inputString.charAt(firstNonZeroIndex) == '0') {
            firstNonZeroIndex++;
        }

        // Return the substring starting from the first non-zero character
        return inputString.substring(firstNonZeroIndex);
    }


    /**
     * 初始化缓存加载
     *
     * @return bool
     */
    @PostConstruct
    public Boolean intoCacheLoad() {
        //删除缓存
        redisHashService.delete(redisKeyName);

        //获取数据
        List<SysCodeRuleDTO> list = this.getList(new SysCodeRuleQuery());

        for(SysCodeRuleDTO ruleDTO : list){
            SysCodeRoleItemQuery query=new SysCodeRoleItemQuery();
            query.setRuleId(ruleDTO.getId());
            List<SysCodeRoleItemDTO> details=sysCodeRoleItemService.getList(query);
            ruleDTO.setDetails(details);
        }

        Map<String, SysCodeRuleDTO> maps = list.stream().collect(Collectors.toMap(k -> k.getRuleCode().toLowerCase(), f -> f));

        //写入到缓存
        redisHashService.setMap(redisKeyName, maps);
        return true;
    }

}

